import type {
  CatchCallbackFn,
  CronCallback,
  CronDate,
  CronOptions,
  ProtectCallbackFn,
} from 'croner'
import { Cron } from 'croner'
/**
 * 定时任务（Cron Job）配置选项(挑选了一些常用的配置项，具体可以查看官方文档进行选择)
 * @interface
 */
export interface CronOptionsZhcn extends CronOptions {
  /**
   * 任务运行的最大次数。
   * 默认值：Infinity（无限次）
   *
   * The maximum number of times the job will run.
   * @default Infinity
   */
  maxRuns?: number

  /**
   * 任务执行之间的最小间隔时间（单位：秒）。
   * 默认值：1
   *
   * The minimum interval between job executions, in seconds.
   * @default 1
   */
  interval?: number
  /**
   * 任务开始运行的日期和时间。（ISO 8601 formatted datetime (2021-10-17T23:43:00)）
   *
   * The date and time at which the job should start running.
   */
  startAt?: string | Date | CronDate

  /**
   * 任务停止运行的日期和时间。（ISO 8601 formatted datetime (2021-10-17T23:44:00)）
   *
   * The date and time at which the job should stop running.
   */
  stopAt?: string | Date | CronDate

  /**
   * 定时任务的时区。例如： 'Asia/Shanghai'
   *
   * The timezone for the cron job.
   */
  timezone?: string
  /**
   * 如果为 true，当前一次执行仍在运行时，阻止任务再次运行。
   * 如果提供回调函数，则会在任务被阻塞时调用该函数。
   * 默认值：false
   *
   * If true, prevents the job from running if the previous execution is still in progress.
   * If a function is provided, it will be called if the job is blocked.
   * @default false
   */
  protect?: boolean | ProtectCallbackFn
  /**
   * 如果为 true，任务初始时将被暂停。
   * 默认值：false
   *
   * If true, the job will be paused initially.
   * @default false
   */
  paused?: boolean
  /**
   * 如果为 true，任务函数抛出的错误将被捕获。
   * 如果提供回调函数，则会在出错时调用该函数，并传入错误和任务实例。
   * 默认值：false
   *
   * If true, errors thrown by the job function will be caught.
   * If a function is provided, it will be called with the error and the job instance.
   * @default false
   */
  catch?: boolean | CatchCallbackFn
}

export class MyCroner {
  static cronMap: Record<string, Cron> = {}

  /**
   * 添加定时任务
   *
   * @param name 任务名称
   * @param pattern 任务执行的时间模式
   * @param options 任务选项
   * @param taskFunction 任务回调函数
   * @throws 如果任务名称已存在，则抛出错误
   */
  static addCron(
    name: string,
    pattern: string,
    options: CronOptionsZhcn,
    taskFunction?: CronCallback,
  ) {
    return new Promise<Cron>((resolve, reject) => {
      setTimeout(() => {
        if (MyCroner.cronMap[name]) {
          return
        }
        try {
          const cron = new Cron(
            pattern,
            {
              ...options,
              catch: (err, job) => {
                console.error('Cron job error:', err)
                if (typeof options.catch == 'function') {
                  options.catch?.(err, job)
                }
              },
            },
            taskFunction,
          )
          MyCroner.cronMap[name] = cron
          resolve(cron)
        }
        catch (error) {
          reject(new Error(`创建定时任务失败: ${error}`))
        }
      }, 0)
    })
  }

  /**
   * 更新指定名称的定时任务
   *
   * @param name 定时任务的名称
   * @param pattern 定时任务的执行模式，例如 '
   */
  static updateCron(
    name: string,
    pattern: string,
    options: CronOptionsZhcn,
    taskFunction?: CronCallback,
  ) {
    if (MyCroner.cronMap[name]) {
      MyCroner.cronMap[name].stop()
    }
    return MyCroner.addCron(
      name,
      pattern,
      options as CronOptionsZhcn,
      taskFunction,
    )
  }

  /**
   * 清除所有定时任务
   * 清除所有已设置的定时任务，并将 MyCroner.cronMap 初始化为空对象
   */
  static deleteCron(name: string) {
    if (MyCroner.cronMap[name]) {
      MyCroner.cronMap[name].stop()
      Reflect.deleteProperty(MyCroner.cronMap, name)
    }
  }

  /**
   * 清除所有定时任务
   * 清除所有已设置的定时任务，并将 MyCroner.cronMap 初始化为空对象
   */
  static deleteAllCrons() {
    for (const cron of Object.values(MyCroner.cronMap)) {
      cron.stop()
    }
    MyCroner.cronMap = {}
  }

  /**
   * 根据名称获取定时任务
   *
   * @param name 定时任务名称
   * @returns 返回对应的定时任务
   * @throws 如果没有找到对应名称的定时任务，则抛出错误
   */
  static getCron(name: string) {
    if (!MyCroner.cronMap[name]) {
      throw new Error(`No cron job found with name ${name}`)
    }
    return MyCroner.cronMap[name]
  }

  /**
   * 获取所有定时任务的映射关系
   *
   * @returns 返回一个包含所有定时任务映射关系的对象
   */
  static getAllCrons() {
    return MyCroner.cronMap
  }
}
